#ifndef TPL_CONCURRENT_GRAPH_H
#define TPL_CONCURRENT_GRAPH_H

#include <pthread.h>
#include <tpl_graph.H>
#include <useMutex.H>

using namespace Aleph;



namespace Aleph {
	
	template <typename Node_Info, typename Arc_Info> class Concurrent_Graph;
	

	template <typename Node_Info> 
struct Concurrent_Node : public Graph_Node<Node_Info> {


  typedef Graph_Node<Node_Info> Parent_Node;
  typedef Node_Info Node_Type;
 
	
  pthread_mutex_t mutex;
   
   	// Constructor a partir de un Nodo padre existente
  Concurrent_Node(Graph_Node<Node_Info> * node) : Parent_Node(node) {
  	init_mutex(mutex);
   }
  
  
   	// Constructor por defecto
	Concurrent_Node() : Parent_Node() {
   	init_mutex(mutex);
	}
   	
   	// Constructor a partir de un Node_Info
	Concurrent_Node(const Node_Info& node_info) : Parent_Node(node_info) {
  	init_mutex(mutex);
	}
   	   	
   	// Constructor a partir de un Nodo existente
	Concurrent_Node(Concurrent_Node * node) : Parent_Node(node) {
  	init_mutex(mutex);
	}
   	
	//Destructor
	~Concurrent_Node() 
	{
		destroy_mutex(mutex);   
	}
	
	Node_Type & get_info() {
  	 CRITICAL_SECTION(mutex);
  	 return Parent_Node::get_info();
  }  
	
};
 
	template <typename Arc_Info> 
struct Concurrent_Arc : public Graph_Arc<Arc_Info> {

  typedef Graph_Arc<Arc_Info> Parent_Arc;
  typedef Arc_Info Arc_Type;

	pthread_mutex_t mutex;

	Concurrent_Arc(Graph_Arc<Arc_Info> * arc) : Parent_Arc(arc){
		init_mutex(mutex);
	}
	
	Concurrent_Arc() : Parent_Arc() {
  	init_mutex(mutex);
	}
    
  Concurrent_Arc(void * src, void * tgt, const Arc_Info & data) : Parent_Arc(src, tgt, data)
  {
  	init_mutex(mutex);
	}
    
  ~Concurrent_Arc() 
  {
		destroy_mutex(mutex);
	}
  
  Arc_Type & get_info() {
  	 CRITICAL_SECTION(mutex);
  	 return Parent_Arc::get_info();
  }  
    
  void * get_connected_node(void * node)
	{
		return Parent_Arc::get_connected_node(node);
	}
};

	template <typename Node, typename Arc> 
class Concurrent_Graph : 
public List_Graph<Node, Arc> {

private:
	pthread_mutex_t mutex;

   	
public:
	typedef List_Graph< Node, Arc > Parent_Graph;
  	
  typedef typename Node::Node_Type Node_Type; 

  typedef typename Arc::Arc_Type Arc_Type;

  	// Constructores
  
  	// Constructor por defecto
	Concurrent_Graph() : Parent_Graph() {
   	init_mutex(mutex);  
  }
  
  // Constructor que copia a partir de otro grafo
  Concurrent_Graph(Concurrent_Graph& g) : Parent_Graph(g) 
  {
   	init_mutex(mutex); 
  }

  // Destructor
  virtual ~Concurrent_Graph() {
   	destroy_mutex(mutex);
  }

  class Node_Iterator  
  {
  	Concurrent_Graph<Node, Arc> & cg;
    typename Parent_Graph::Node_Iterator parent_iterator;

	public:

    
		Node_Iterator() : parent_iterator() 
		{ 
		}

    Node_Iterator(Concurrent_Graph & _g) : cg(_g), parent_iterator(_g)
    {
    }

		Node_Iterator(const Node_Iterator & it) : cg(it.cg), parent_iterator(it.parent_iterator)
		{
		}

    Node_Iterator & operator = (const Node_Iterator & it) 
    {
			CRITICAL_SECTION(cg.mutex);
			parent_iterator = it.parent_iterator;
			return *this;
		}
    
    Node * get_current_node() 
    {
			CRITICAL_SECTION(cg.mutex);
    	Node * node = parent_iterator.get_current_node();
			return node;
		}
	
		bool has_current() {
			CRITICAL_SECTION(cg.mutex);
			return parent_iterator.has_current();
		}
	
		void next() {
			CRITICAL_SECTION(cg.mutex);
			parent_iterator.next();
		}
	};

  class Arc_Iterator 
  {
  	Concurrent_Graph<Node, Arc> & cg;
		typename Parent_Graph::Arc_Iterator parent_iterator;

  public:

		Arc_Iterator() : parent_iterator() 
		{ 
    }

    Arc_Iterator(Concurrent_Graph & _g) : cg(_g), parent_iterator(_g)
		{
		}

		Arc_Iterator(const Arc_Iterator & it) : cg(it.cg), parent_iterator(it.parent_iterator)
		{
		}
	
		Arc_Iterator & operator = (const Arc_Iterator & it) 
		{
			CRITICAL_SECTION(cg.mutex);
			parent_iterator = it.parent_iterator;
			return *this;
		};

		Arc * get_current_arc() 
		{ 
			CRITICAL_SECTION(cg.mutex);
			Arc * arc = parent_iterator.get_current_arc();
			return arc;
		};
    
		bool has_current() 
		{
			CRITICAL_SECTION(cg.mutex);
			return parent_iterator.has_current();
		};
	
		void next() 
		{
			CRITICAL_SECTION(cg.mutex);
			parent_iterator.next();
		}
	};

	struct Node_Arc_Iterator : public Parent_Graph::Node_Arc_Iterator
	{
	public:

		Node_Arc_Iterator() : Parent_Graph::Node_Arc_Iterator() 
		{ 
			/* empty */ 
		}

		Node_Arc_Iterator(Node * _src_node) : Parent_Graph::Node_Arc_Iterator(_src_node)
		{
			// empty
		}

		Node_Arc_Iterator(const Node_Iterator& it) : Parent_Graph::Node_Arc_Iterator(it) 
		{ 
			/* empty */ 
		}
                

    Arc * get_current_arc() 
    {
      return Parent_Graph::Node_Arc_Iterator::get_current_arc();
    }

		Node * get_tgt_node() 
		{ 
			return Parent_Graph::Node_Arc_Iterator::get_tgt_node();
		}
	};

	// m�todos que manipulan nodos

	const long & get_num_arcs(Node * node) const 
	{ 
		CRITICAL_SECTION(node->mutex);
		return Parent_Graph::get_num_arcs(node); 
	}

	void reset_bit(Node * node, const int & bit) 
	{
		CRITICAL_SECTION(node->mutex);
		Parent_Graph::reset_bit(node, bit); 
	}

	Bit_Fields & get_control_bits(Node * node) 
	{ 
		CRITICAL_SECTION(node->mutex);
		return Parent_Graph::get_control_bits(node); 
	}

	void set_bit(Node * node, const int & bit, const int & value) 
	{
		CRITICAL_SECTION(node->mutex);
		Parent_Graph::set_bit(node, bit, value);
	}

	long & get_counter(Node * node) 
	{ 
		CRITICAL_SECTION(node->mutex);
		return Parent_Graph::get_counter(node); 
	}

	void reset_counter(Node * node) 
	{ 
		CRITICAL_SECTION(node->mutex);
		Parent_Graph::reset_counter; 
	}

	void *& get_cookie(Node * node) 
	{ 
		CRITICAL_SECTION(node->mutex);
		return Parent_Graph::get_cookie(node); 
	}

	Node * get_src_node(Arc * arc) 
	{
		CRITICAL_SECTION(arc->mutex);
		return Parent_Graph::get_src_node(arc); 
	}

	Node * get_tgt_node(Arc * arc) 
	{ 
		CRITICAL_SECTION(arc->mutex);
		return Parent_Graph::get_tgt_node(arc); 
	}

	Node * get_connected_node(Arc * arc, Node * node) 
	{
		CRITICAL_SECTION(arc->mutex);
		return Parent_Graph::get_connected_node(arc, node);
	}

	const bool node_belong_to_arc(Arc * arc, Node * node) const
	{
		CRITICAL_SECTION(arc->mutex);
		return Parent_Graph::node_belong_to_arc(arc, node);
	}

	void reset_bit(Arc * arc, const int & bit) 
	{
		CRITICAL_SECTION(arc->mutex);
		Parent_Graph::reset_bit(arc, bit); 
	}

	Bit_Fields & get_control_bits(Arc * arc) 
	{ 
		CRITICAL_SECTION(arc->mutex);
		return Parent_Graph::get_control_bits(arc); 
	}

	void set_bit(Arc * arc, const int & bit, const int & value) 
	{
		CRITICAL_SECTION(arc->mutex);
		Parent_Graph::set_bit(arc, bit, value);
	}

	long & get_counter(Arc * arc) 
	{ 
		CRITICAL_SECTION(arc->mutex);
		return Parent_Graph::get_counter(arc); 
	}

	void reset_counter(Arc * arc) 
	{ 
		CRITICAL_SECTION(arc->mutex);
		Parent_Graph::counter(arc); 
	}

	void *& get_cookie(Arc * arc) 
	{ 
		CRITICAL_SECTION(arc->mutex);
		return Parent_Graph::get_cookie(); 
 	}

	long get_num_nodes()  
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::get_num_nodes(); 
	}
  	
	long get_num_arcs() {
		CRITICAL_SECTION(mutex); 
		return Parent_Graph::get_num_arcs(); 
	}
 
	Node * search_node(const Node_Type & node_info)
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::search_node(node_info);
	} 
	
	Node * insert_node(const Node_Type & node_info)
	{
		CRITICAL_SECTION(mutex);
		Node * ret_val = new Node(node_info);
		Parent_Graph::insert_node(ret_val);
		return ret_val;
	}

	Node * get_first_node() 
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::get_first_node();
	}
	
	void reset_bit_arcs(const int & bit)
	{ 
		CRITICAL_SECTION(mutex); 
		Parent_Graph::reset_bit_arcs(bit); 
	}
	
	void reset_bit_nodes(const int & bit) 
	{
		CRITICAL_SECTION(mutex); 
		Parent_Graph::reset_bit_nodes(bit); 
	}

	Arc * get_first_arc() 
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::get_first_arc(); 
	}
	
	void verify_graphs(Concurrent_Graph& g) 
	{
		CRITICAL_SECTION(mutex); 
		Parent_Graph::verify_graphs(g);		
	}
	
	const bool is_digraph() 
	{  
		CRITICAL_SECTION(mutex);
		return Parent_Graph::is_digraph(); 
	}
	  
	Concurrent_Graph& operator = (Concurrent_Graph & g)
	{
		CRITICAL_SECTION(mutex);
		if (this == &g)
			return *this;

		copy_graph(*this, const_cast<Concurrent_Graph&>(g));

		return *this; 	
	}
	  
	void insert_node(Node * node) 
	{
		CRITICAL_SECTION(mutex);
		Parent_Graph::insert_node(node);			
	}
	   
  
	void remove_node(Node * node)
	{
		CRITICAL_SECTION(mutex);
		Parent_Graph::remove_node(node);  		
	}
  
	void reset_counter_nodes()
	{
		CRITICAL_SECTION(mutex);
		Parent_Graph::reset_counter_nodes();
	}
  
	void reset_counter_arcs()
	{
		CRITICAL_SECTION(mutex);
		Parent_Graph::reset_counter_arcs();
	}
  
    template <class Operation> void operate_nodes()
  {
		CRITICAL_SECTION(mutex);
    for (typename Parent_Graph::Node_Iterator itor(*this); itor.has_current(); itor.next())
      Operation () (*this, itor.get_current_node());
  }

  template <class Operation> void operate_arcs()
  {
		CRITICAL_SECTION(mutex);
    for (typename Parent_Graph::Arc_Iterator itor(*this); itor.has_current(); itor.next())
      Operation () (*this, itor.get_current_arc());
  }

  template <class Operation> void operate_nodes(void * ptr)
  {
		CRITICAL_SECTION(mutex);
    for (typename Parent_Graph::Node_Iterator itor(*this); itor.has_current(); itor.next())
      Operation (ptr) (*this, itor.get_current_node());
  }

  template <class Operation> void operate_arcs(void * ptr)
  {
		CRITICAL_SECTION(mutex);
    for (typename Parent_Graph::Arc_Iterator itor(*this); itor.has_current(); itor.next())
      Operation (ptr) (*this, itor.get_current_arc());
  }

  template <class Operation> void operate_arcs(Node * node)
  {
		CRITICAL_SECTION(mutex);
    for (typename Parent_Graph::Node_Arc_Iterator it(*this, node); it.has_current(); it.next())
      Operation () (*this, it.get_current_arc());
  }

  template <class Operation> void operate_arcs(Node * node, void * ptr)
  {
		CRITICAL_SECTION(mutex);
    for (typename Parent_Graph::Node_Arc_Iterator it(*this, node); it.has_current(); it.next())
      Operation (ptr) (*this, it.get_current_arc());
  }
  
  template <class Operation> void operate_on_graph (void * cookie) 
  {
  	CRITICAL_SECTION(mutex);
  	Operation () (*this, cookie);
  }
  
  template <class Operation> void operate_on_arc (Arc * arc, void * cookie) 
  {
  	CRITICAL_SECTION(arc->mutex);
  	Operation () (*this, arc, cookie);
  }
  
  template <class Operation> void operate_on_node (Node * node, void * cookie) 
  {
  	CRITICAL_SECTION(node->mutex);
  	Operation () (*this, node, cookie);
  }

	const bool node_in_graph(Node * node) 
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::node_in_graph(node);
	}
  
    
	template <class Compare>
	void sort_arcs()
	{
		CRITICAL_SECTION(mutex);
		Parent_Graph::template sort_arcs<Compare>();
	}

	Arc * insert_arc(Node *  src_node, 
									Node * tgt_node, 
									const Arc_Type & arc_info) 
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::insert_arc(src_node, tgt_node, arc_info);    	
	}

	void remove_arc(Arc * arc)
	{
		CRITICAL_SECTION(mutex);
		Parent_Graph::remove_arc(arc);
	}
    
    
	Arc * search_arc(Node * src_node, Node * tgt_node) 
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::search_arc(src_node, tgt_node);    	
	}
    
	Arc * search_arc(const Arc_Type & arc_info)
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::search_arc(arc_info);    	
	}
  
	const bool arc_in_graph(Arc * arc) 
	{
		CRITICAL_SECTION(mutex);
		return Parent_Graph::arc_in_graph(arc);
	}
};

}

#endif /*TPL_CONCURRENT_GRAPH_H*/
